﻿using System;
using System.Collections.Generic;
using System.Linq;
using StockholmsStad.Data.Context;
using StockholmsStad.Data.ContractFactory;
using StockholmsStad.Data.Entity;
using StockholmsStad.Data.Interfaces;
using StockholmsStad.Data.Properties;
using StockholmsStad.Runtime.Serialization.DataContract;
using StockholmsStad.Utilities.Enums;
using StockholmsStad.Utilities.Exceptions;
using Ceremony=StockholmsStad.Runtime.Serialization.DataContract.Ceremony;
using ParVisProperties=StockholmsStad.Runtime.Serialization.DataContract.ParVisProperties;
using StockholmsStad.Runtime.Serialization.DataContract.Enums;
using Minister=StockholmsStad.Runtime.Serialization.DataContract.Minister;
using Ticket=StockholmsStad.Runtime.Serialization.DataContract.Ticket;

namespace StockholmsStad.Data.DatabaseAgents
{
    internal class CeremonyAgent : DBAgentBase, ICeremonyAgent
    {
        #region Implementation of ICeremonyAgent

        public int AddCeremony(IDataContext context, Ceremony ceremony)
        {
            CeremonyFactory factory = FactoryAgent.GetCeremonyFactory();

            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                int ceremonyLanguageId = GetPrimaryKeyForCeremonyLanguage(context, ceremony.CeremonyLanguage.Name);
                int certificateLanguageId = GetPrimaryKeyForCertificateLanguage(context, ceremony.CertificateLanguage.Name);

                Entity.Ceremony entityCeremony = factory.CreateEntityObject(0, ceremony, 0, ceremonyLanguageId, certificateLanguageId);
                int ceremonyId = Insert(entityCeremony, dbCtx);

                if (ceremony.SpecialNeeds != null)
                {
                    foreach(var cerSpecialNeed in ceremony.SpecialNeeds)
                    {
                        var specialNeed = dbCtx.SpecialNeeds.Where(s => s.Name == cerSpecialNeed.Name).SingleOrDefault();
                        var newNeed = new CeremonySpecialNeed
                                          {CeremonyId = ceremonyId, SpecialNeedId = specialNeed.SpecialNeedId};
                        dbCtx.CeremonySpecialNeeds.InsertOnSubmit(newNeed);
                        dbCtx.SubmitChanges();
                    }
                }

                return ceremonyId;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public int GetCeremonyId(IDataContext context, int ticketId)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                var ticket = dbCtx.Tickets.Where(t => t.TicketId == ticketId).FirstOrDefault();

                if (ticket == null)
                    throw new StockholmStadSystemException(SystemErrorCode.CeremonyNotExists);

                return ticket.CeremonyId;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public Ceremony GetCeremony(IDataContext context, int ceremonyId)
        {
            CeremonyFactory factory = FactoryAgent.GetCeremonyFactory();

            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                var ceremony = dbCtx.Ceremonies.Where(c => c.CeremonyId == ceremonyId).SingleOrDefault();

                if (ceremony == null)
                    return null;

                IEnumerable<CeremonySpecialNeed> specialNeed = GetSpecialNeedsFromCeremonyId(ceremonyId, dbCtx);
                var ceremonyLanguage = dbCtx.GetCeremonyLanguage(ceremonyId).FirstOrDefault();
                var certificateLanguage = dbCtx.GetCertificateLanguage(ceremonyId).FirstOrDefault();

                var otherLang = dbCtx.Languages.Where(l => l.LanguageId == ceremony.RequestedLanguageId).FirstOrDefault();

                Ceremony contractCeremony = factory.CreateContractObject(ceremony, specialNeed,
                    ceremonyLanguage, certificateLanguage, otherLang);

                return contractCeremony;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public void DeleteCeremony(IDataContext context, int ceremonyId)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                var ceremony = dbCtx.Ceremonies.Where(c => c.CeremonyId == ceremonyId).SingleOrDefault();
                IEnumerable<CeremonySpecialNeed> specialNeeds = GetSpecialNeedsFromCeremonyId(ceremonyId, dbCtx);
                dbCtx.CeremonySpecialNeeds.DeleteAllOnSubmit(specialNeeds);
                dbCtx.Ceremonies.DeleteOnSubmit(ceremony);
                dbCtx.SubmitChanges();
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public void RemoveImpedimentsDates(IDataContext context, int ceremonyId)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                var ceremony = dbCtx.Ceremonies.Where(id => id.CeremonyId == ceremonyId).FirstOrDefault();

                if(ceremony == null)
                    throw  new StockholmStadSystemException(SystemErrorCode.CeremonyNotExists);

                ceremony.ImpedimentDocIssuedDate = null;
                ceremony.ImpedimentDocAllowDelayDays = 0;
                dbCtx.SubmitChanges();
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }

        }

        public void UpdateCeremony(IDataContext context, int ceremonyId, Ceremony ceremony)
        {
            try
            {
                int ceremonyLanguageId = GetPrimaryKeyForCeremonyLanguage(context, ceremony.CeremonyLanguage.Name);
                int certificateLanguageId = GetPrimaryKeyForCertificateLanguage(context,
                                                                                ceremony.CertificateLanguage.Name);

                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                UpdateSpecialNeeds(ceremony, dbCtx, ceremonyId);

                var ceremonyEntity = dbCtx.Ceremonies.Where(c => c.CeremonyId == ceremonyId).SingleOrDefault();

                UpdateRequestedLanguage(ceremony, dbCtx, ceremonyEntity);

                ceremonyEntity.CeremonyLanguageId = ceremonyLanguageId;
                ceremonyEntity.CertificateLanguageId = certificateLanguageId;
                ceremonyEntity.Interpreter = ceremony.Interpreter;
                ceremonyEntity.Note = ceremony.Note;
                dbCtx.SubmitChanges();
            }
            catch (StockholmStadSystemException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }
        public void UpdateCeremonyBookingDetails(IDataContext context, int ceremonyId,
            Ceremony ceremony, Runtime.Serialization.DataContract.Booking booking)
        {
            StockholmsStadDataContext dbCtx = context.GetInternalContext();

            var ceremonyEntity = dbCtx.Ceremonies.
                Where(c => c.CeremonyId == ceremonyId).FirstOrDefault();

            SetImpedimentDates(context, booking, ceremony, ceremonyEntity);
            dbCtx.SubmitChanges();
        }

        public void SendConfirmedBookingMail(IDataContext context, int ceremonyId, Ticket ticket, DateTime bookingDate)
        {
            SendConfirmedBookingMail(context, ceremonyId, ticket, bookingDate, false);
        }

        public void SendConfirmedBookingMail(IDataContext context, int ceremonyId, Ticket ticket, DateTime bookingDate, bool forceSendMail)
        {
            StockholmsStadDataContext dbCtx = context.GetInternalContext();

            var ceremonyEntity = dbCtx.Ceremonies.
                Where(c => c.CeremonyId == ceremonyId).FirstOrDefault();

            // Skicka mail HP inkommit eller skapa notis om mailadress saknas
            // Kolla om vi alltid ska skicka mail eller om vi redan har skickat
            if (forceSendMail || (!ceremonyEntity.ImpedimentDocReceivedMailSent && ceremonyEntity.ImpedimentDocIssuedDate.HasValue))
            {
                var ticketAgent = AgentFactory.GetTicketAgent();

                if (ticket.Couple.PersonDetails != null && !string.IsNullOrEmpty(ticket.Couple.PersonDetails.Email))
                {
                    dbCtx.SendConfirmedBookingMail(ticket.Couple.PersonDetails.Email, bookingDate);
                    ceremonyEntity.ImpedimentDocReceivedMailSent = true;

                    // TODO: borde vara administratören istället för System
                    ticketAgent.InsertHistory(context, ticket.TicketId, 
                                                string.Format(Resources.ConfirmedBookingEmailSent,
                                                                ticket.Couple.PersonDetails.Email),
                                                "System", true);
                }
                else
                {
                    // TODO: borde vara administratören istället för System
                    ticketAgent.InsertNotice(context, ticket.TicketId,
                                             string.Format(Resources.ImpedimentDocReceivedEmailMissing, bookingDate,
                                                           ticket.Couple.Person1.FirstNames,
                                                           ticket.Couple.Person1.LastName,
                                                           ticket.Couple.Person2.FirstNames,
                                                           ticket.Couple.Person2.LastName), "System");

                    ceremonyEntity.ImpedimentDocReceivedMailSent = true;
                }
            }      
      
            dbCtx.SubmitChanges();
        }

        public List<ValueIdPair> GetSpecialNeeds(IDataContext context)
        {
            var list = new List<ValueIdPair>();

            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                List<SpecialNeed> ceremonySpecialNeeds = SelectAll<SpecialNeed>(dbCtx);

                foreach (SpecialNeed specialNeed in ceremonySpecialNeeds)
                {
                    list.Add(new ValueIdPair
                    {
                        Id = specialNeed.SpecialNeedId,
                        Name = specialNeed.Name
                    });
                }


                return list.OrderBy(sp => sp.Name).ToList();
            }
            catch (Exception)
            {
                return new List<ValueIdPair>();
            }
        }

        public int AddSpecialNeed(IDataContext context, string specialNeed)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                SpecialNeed languageExists = SelectAll<SpecialNeed>(dbCtx).Find(l => l.Name.Equals(specialNeed));

                if (languageExists != null)
                    throw new StockholmStadSystemException(SystemErrorCode.SpecialNeedAlreadyExists);

                CeremonyFactory ceremonyFactory = FactoryAgent.GetCeremonyFactory();
                SpecialNeed entitySpecialNeed = ceremonyFactory.CreateEntitySpecialNeed(specialNeed);
                return Insert(entitySpecialNeed, dbCtx);
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }
        }

        public void ResetCeremonyFlags(IDataContext context, int ticketId)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                Entity.Ceremony ceremony = (from c in dbCtx.Ceremonies
                                            join t in dbCtx.Tickets on c.CeremonyId equals t.CeremonyId
                                            where t.TicketId == ticketId
                                            select c).FirstOrDefault();
                if (ceremony == null)
                    throw new StockholmStadSystemException(SystemErrorCode.CeremonyNotExists);

                ceremony.ImpedimentDocReminderEmailSent = false;
                ceremony.RemoveBookingReminderCreated = false;
                dbCtx.SubmitChanges();
            }
            catch (StockholmStadSystemException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public List<CeremonySummary> GetIndividualCeremonyBooking(IDataContext context, DateTime dateTime)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                var factory = new CeremonyFactory();
                var resultList = dbCtx.GetIndividualCeremonyBookingForDate(dateTime).Select(r => factory.CreateContractObject(r)).ToList();

                return resultList;
            }
            catch (StockholmStadSystemException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public List<CeremonySummary> GetIndividualCeremonyBooking(IDataContext context, DateTime fromDate, DateTime? toDate, int? ministerId)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                var factory = new CeremonyFactory();

                List<CeremonySummary> ceremonies;

                var resultList = dbCtx.GetIndividualCeremonyBookingForDate(fromDate.Date).ToList();

                if (toDate.HasValue && ministerId.HasValue)
                {
                    ceremonies = resultList.
                        Where(b => b.Date.Date.CompareTo(toDate) <= 0 && b.MinisterId == ministerId).
                        Select(r => factory.CreateContractObject(r)).ToList();
                }
                else if (ministerId.HasValue)
                {
                    ceremonies = resultList.
                        Where(b => b.MinisterId == ministerId).
                        Select(r => factory.CreateContractObject(r)).ToList();
                }
                else if (toDate.HasValue)
                {
                    ceremonies = resultList.
                        Where(b => b.Date.Date.CompareTo(toDate) <= 0).
                        Select(r => factory.CreateContractObject(r)).ToList();
                }
                else
                {
                    ceremonies = resultList.Select(r => factory.CreateContractObject(r)).ToList();
                }

                return ceremonies;
            }
            catch (StockholmStadSystemException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public List<CeremonySummaryDetails> GetIndividualCeremonyBookingDetails(IDataContext context, DateTime dateTime, int? ministerId)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                List<GetIndividualCeremonyBookingDetailsResult> resultList;

                if (ministerId.HasValue)
                {
                    resultList = dbCtx.GetIndividualCeremonyBookingDetails(dateTime).Where(c => c.MinisterId == ministerId.Value).ToList();
                }
                else
                {
                    resultList = dbCtx.GetIndividualCeremonyBookingDetails(dateTime).ToList();
                }
                    
                var bookingDetails = new List<CeremonySummaryDetails>();
                foreach (var item in resultList)
                {

                    var ceremonyDetails = new CeremonySummaryDetails
                                              {
                                                  TicketId = item.TicketId,
                                                  CoupleId = item.CoupleId,
                                                  MinisterId = item.MinisterId,
                                                  StartDateTime = item.Date,
                                                  Ceremony = GetCeremony(context, item.CeremonyId),
                                                  Person1 =
                                                      new Runtime.Serialization.DataContract.Person
                                                          {
                                                              FirstNames = item.Person1FirstNames,
                                                              LastName = item.Person1LastName
                                                          },
                                                  Person2 =
                                                      new Runtime.Serialization.DataContract.Person
                                                          {
                                                              FirstNames = item.Person2FirstNames,
                                                              LastName = item.Person2LastName
                                                          },
                                                  TicketStatusType = (TicketStatusType) item.ticketStatusId,
                                                  Minister = new Minister()
                                                            {
                                                                FirstNames = item.MinisterFirstNames,
                                                                LastName = item.MinisterLastName
                                                            },
                                                  // Ny lag from 2009-05-01
                                                  MarriageCertificateType = MarriageCertificateType.Wedding,
                                              };

                    bookingDetails.Add(ceremonyDetails);
                }

                return bookingDetails;
            }
            catch (StockholmStadSystemException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public List<CeremonySummary> GetOrdinaryCeremonyBooking(IDataContext context, DateTime dateTime)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                var factory = new CeremonyFactory();
                var resultList = dbCtx.GetOrdinaryCeremonyBookingForDate(dateTime).Select(r => factory.CreateContractObject(r)).ToList();

                return resultList;
            }
            catch (StockholmStadSystemException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public List<CeremonySummary> GetOrdinaryCeremonyBooking(IDataContext context, DateTime fromDate, DateTime? toDate, int? ministerId)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                var factory = new CeremonyFactory();

                List<CeremonySummary> ceremonies;

                var resultList = dbCtx.GetOrdinaryCeremonyBookingForDate(fromDate).ToList();
                
                if (toDate.HasValue && ministerId.HasValue)
                {
                    ceremonies = resultList.
                        Where(b => b.StartDate.Value.CompareTo(toDate) <= 0 && b.MinisterId == ministerId).
                        Select(r => factory.CreateContractObject(r)).ToList();
                }
                else if(ministerId.HasValue)
                {
                    ceremonies = resultList.
                        Where(b => b.MinisterId == ministerId).
                        Select(r => factory.CreateContractObject(r)).ToList();
                }
                else if(toDate.HasValue)
                {
                    ceremonies = resultList.
                        Where(b => b.StartDate.Value.CompareTo(toDate) <= 0).
                        Select(r => factory.CreateContractObject(r)).ToList();                    
                }
                else
                {
                    ceremonies = resultList.Select(r => factory.CreateContractObject(r)).ToList();
                }

                return ceremonies;
            }
            catch (StockholmStadSystemException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public List<CeremonySummaryDetails> GetOrdinaryCeremonyBookingDetails(IDataContext context, DateTime dateTime)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                var resultList = dbCtx.GetOrdinaryCeremonyBookingDetails(dateTime).ToList();

                var bookingDetails = new List<CeremonySummaryDetails>();
                foreach (var item in resultList)
                {
                    var bookingDetail = new CeremonySummaryDetails
                                            {
                                                TicketId = item.TicketId,
                                                CoupleId = item.CoupleId,
                                                StartDateTime = item.StartDateTime,
                                                IntervalMinutes = item.IntervalMinutes,
                                                Ceremony = GetCeremony(context, item.CeremonyId),
                                                Person1 =
                                                    new Runtime.Serialization.DataContract.Person
                                                        {
                                                            FirstNames = item.Person1FirstNames,
                                                            LastName = item.Person1LastName
                                                        },
                                                Person2 =
                                                    new Runtime.Serialization.DataContract.Person
                                                        {
                                                            FirstNames = item.Person2FirstNames,
                                                            LastName = item.Person2LastName
                                                        },
                                                TicketStatusType = (TicketStatusType)item.ticketStatusId,
                                            };

                    // Ny lag from 2009-05-01
                    bookingDetail.MarriageCertificateType = MarriageCertificateType.Wedding;

                    if (item.MinisterId.HasValue)
                    {
                        bookingDetail.MinisterId = item.MinisterId.Value;
                        bookingDetail.Minister = new Minister()
                                     {
                                        FirstNames = item.MinisterFirstNames,
                                        LastName = item.MinisterLastName
                                     };
                    }
                    
                    bookingDetails.Add(bookingDetail);
                }

                return bookingDetails;
            }
            catch (StockholmStadSystemException e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.CeremonyNotExists, e);
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public DateTime GetNextCeremonyDateForIndividualService(IDataContext context)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                var result = dbCtx.GetNextCeremonyDateForIndividualService().SingleOrDefault();

                if (result != null && result.Date.HasValue)
                    return result.Date.Value;

                return new DateTime();
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }
        }

        public DateTime GetNextCeremonyDateForOrdinaryService(IDataContext context)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                var result = dbCtx.GetNextCeremonyDateForOrdinaryService().SingleOrDefault();

                if (result != null && result.Date.HasValue)
                    return result.Date.Value;

                return new DateTime();
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }
        }

        public DateTime GetPreviousCeremonyDateForIndividualService(IDataContext context)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                var result = dbCtx.GetPreviousCeremonyDateForIndividualService().SingleOrDefault();

                if (result != null && result.Date.HasValue)
                    return result.Date.Value;

                return new DateTime();
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }
        }

        public DateTime GetPreviousCeremonyDateForOrdinaryService(IDataContext context)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                var result = dbCtx.GetPreviousCeremonyDateForOrdinaryService().SingleOrDefault();

                if (result != null && result.Date.HasValue)
                    return result.Date.Value;

                return new DateTime();
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }
        }

        public void GetImpedimentDates(IDataContext context, Runtime.Serialization.DataContract.Booking booking, 
            Ceremony contractCeremony)
        {
            DateTime? impedimentDocLatestDate = null;
            ParVisProperties properties = AgentFactory.GetPropertiesAgent().GetProperties(context);
            DateTime? impedimentDocEarliestDate =
                booking.DateTime != null ?
                    booking.DateTime.StartDateTime.AddMonths(-properties.ImpedimentDocValidMonths) :
                    (DateTime?)null;

            if (booking.CeremonyType == CeremonyType.Ordinary)
            {
                impedimentDocLatestDate =
                    booking.DateTime != null ?
                        booking.DateTime.StartDateTime.AddDays(
                            -properties.OrdinaryImpedimentDocRemoveBookingAtDays) :
                        (DateTime?)null;
            }

            if (booking.CeremonyType == CeremonyType.Individual)
            {
                impedimentDocLatestDate =
                    booking.DateTime.StartDateTime.AddDays(
                        -properties.IndividualImpedimentDocRemoveBookingAtDays);
            }

            if (contractCeremony.ImpedimentDocAllowDelayDays > 0 && impedimentDocLatestDate.HasValue)
            {
                // Lägg ev. till de extra dagar som finns
                impedimentDocLatestDate = impedimentDocLatestDate.Value.AddDays(contractCeremony.ImpedimentDocAllowDelayDays);
            }

            contractCeremony.ImpedimentDocEarliestDate = impedimentDocEarliestDate;

            if (impedimentDocLatestDate.HasValue)
                contractCeremony.ImpedimentDocLatestDate = impedimentDocLatestDate.Value;
        }

        #endregion

        #region Helper methods

        private void UpdateRequestedLanguage(Ceremony ceremony, StockholmsStadDataContext dbCtx, Entity.Ceremony ceremonyEntity)
        {
            if (ceremonyEntity.RequestedLanguageId.HasValue && string.IsNullOrEmpty(ceremony.OtherLanguage))
            {
                ceremonyEntity.RequestedLanguageId = null;
            }

            if (!string.IsNullOrEmpty(ceremony.OtherLanguage))
            {
                var language = dbCtx.Languages.Where(l => l.Name == ceremony.OtherLanguage).FirstOrDefault();

                if (language != null)
                {
                    ceremonyEntity.RequestedLanguageId = language.LanguageId;
                }
                else
                {
                    var newLang = new Language { Name = ceremony.OtherLanguage };
                    var langId = Insert(newLang, dbCtx);
                    ceremonyEntity.RequestedLanguageId = langId;
                }
            }
        }

        private static void UpdateSpecialNeeds(Ceremony ceremony, StockholmsStadDataContext dbCtx, int ceremonyId)
        {
            var existingNeeds =
                (from d in dbCtx.CeremonySpecialNeeds where d.CeremonyId == ceremonyId select d).ToList();

            if (ceremony.SpecialNeeds != null)
            {
                var possibleToDeleteIntArray = (from d in ceremony.SpecialNeeds select d.Id).ToList();

                var deleteNeeds = (from d in existingNeeds
                                   where possibleToDeleteIntArray.IndexOf(d.SpecialNeedId) == -1
                                   select d).ToList();

                foreach (var delItem in deleteNeeds)
                {
                    dbCtx.CeremonySpecialNeeds.DeleteOnSubmit(delItem);
                }

                var intArrToAdd =
                    (from d in dbCtx.CeremonySpecialNeeds where d.CeremonyId == ceremonyId select d.SpecialNeedId).
                        ToList();
                var addedNeeds = (from d in ceremony.SpecialNeeds
                                  where intArrToAdd.IndexOf(d.Id) == -1
                                  select d).ToList();

                foreach (var item in addedNeeds)
                {
                    var specialNeed = dbCtx.SpecialNeeds.Where(sn => sn.Name == item.Name).FirstOrDefault();
                    // Eftersom ceremonyspecial är en många-många tabell så är primärnyckeln en kombination 
                    // av ceremoniid och specialneedid, därför gör vi så här....
                    var newNeed = new CeremonySpecialNeed { CeremonyId = ceremonyId, SpecialNeedId = specialNeed.SpecialNeedId };
                    dbCtx.CeremonySpecialNeeds.InsertOnSubmit(newNeed);
                }
            }
            else
            {
                foreach (var exItem in existingNeeds)
                {
                    dbCtx.CeremonySpecialNeeds.DeleteOnSubmit(exItem);
                }
            }
            dbCtx.SubmitChanges();
        }

        private void SetImpedimentDates(IDataContext context, Runtime.Serialization.DataContract.Booking booking, Ceremony contractCeremony, Entity.Ceremony entityCeremony)
        {
            if (booking == null)
            {
                // Bokning har makulerats, ta bort ceremonidatum
                if (entityCeremony.ImpedimentDocIssuedDate.HasValue)
                    entityCeremony.ImpedimentDocIssuedDate = null;

                if (entityCeremony.ImpedimentDocAllowDelayDays != 0)
                    entityCeremony.ImpedimentDocAllowDelayDays = 0;
            }
            else
            {
                if (contractCeremony.ImpedimentDocIssuedDate.HasValue)
                {
                    entityCeremony.ImpedimentDocIssuedDate = contractCeremony.ImpedimentDocIssuedDate;
                }

                DateTime? impedimentDocLatestDate;
                ParVisProperties properties = AgentFactory.GetPropertiesAgent().GetProperties(context);

                // om latest day inte har ett värde behövs inte antal försenade dagar sättas, 
                // eftersom default värdet då gäller och sätts utifrån bokningsdatumet
                if (contractCeremony.ImpedimentDocLatestDate.HasValue)
                {
                    if (booking.CeremonyType == CeremonyType.Individual)
                    {
                        impedimentDocLatestDate = booking.DateTime.StartDateTime.AddDays(
                            -properties.IndividualImpedimentDocRemoveBookingAtDays);

                        if (contractCeremony.ImpedimentDocLatestDate.Value.CompareTo(impedimentDocLatestDate.Value.Date) >= 0)
                        {
                            // LatestDay har skjutits upp - Lägg till de extra dagar som är satta
                            TimeSpan span =
                                contractCeremony.ImpedimentDocLatestDate.Value.Subtract(
                                    //Jämför med Date för att ta bort tiden
                                    impedimentDocLatestDate.Value.Date);
                            entityCeremony.ImpedimentDocAllowDelayDays = span.Days;
                        }
                    }
                    else
                    {
                        impedimentDocLatestDate = booking.DateTime.StartDateTime.AddDays(
                            -properties.OrdinaryImpedimentDocRemoveBookingAtDays);

                        if (contractCeremony.ImpedimentDocLatestDate.Value.CompareTo(impedimentDocLatestDate.Value.Date) >= 0)
                        {
                            // LatestDay har skjutits upp - Lägg till de extra dagar som är satta
                            TimeSpan span =
                                contractCeremony.ImpedimentDocLatestDate.Value.Subtract(
                                    //Jämför med Date för att ta bort tiden
                                    impedimentDocLatestDate.Value.Date);
                            entityCeremony.ImpedimentDocAllowDelayDays = span.Days;
                        }
                    }
                }
            }
        }

        private static IEnumerable<CeremonySpecialNeed> GetSpecialNeedsFromCeremonyId(int ceremonyId,
                                                                               StockholmsStadDataContext dbCtx)
        {
            return (from sp in dbCtx.CeremonySpecialNeeds
                    where sp.CeremonyId == ceremonyId
                    select sp).AsEnumerable();
        }

        private int GetPrimaryKeyForCeremonyLanguage(IDataContext context, string languageName)
        {
            ILanguageAgent agent = AgentFactory.GetLanguageAgent();
            ValueIdPair language = agent.GetLanguage(context, languageName);

            if (language == null)
                throw new StockholmStadSystemException(SystemErrorCode.LanguageDoesNotExist);

            CeremonyLanguage lang;

            // Borde finnas i minnet
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                lang = SelectAll<CeremonyLanguage>(dbCtx).Find(p => p.LanguageId == language.Id);
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }

            if (lang == null)
                throw new StockholmStadSystemException(SystemErrorCode.CeremonyLanguageDoesNotExist);

            return lang.CeremonyLanguageId;
        }

        private int GetPrimaryKeyForCertificateLanguage(IDataContext context, string languageName)
        {
            ILanguageAgent agent = AgentFactory.GetLanguageAgent();
            ValueIdPair language = agent.GetLanguage(context, languageName);

            if (language == null)
                throw new StockholmStadSystemException(SystemErrorCode.LanguageDoesNotExist);

            CertificateLanguage lang;

            // Borde finnas i minnet
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                lang = SelectAll<CertificateLanguage>(dbCtx).Find(p => p.LanguageId == language.Id);
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }

            if (lang == null)
                throw new StockholmStadSystemException(SystemErrorCode.CertificateLanguageDoesNotExist);

            return lang.CertificateLanguageId;
        }


        #endregion

        #region ICeremonyAgent Members


        public void UpdateCeremonyWithProtectedIdentity(IDataContext context, int ceremonyId)
        {
            try
            {
                var dbCtx = context.GetInternalContext();

                var ceremonyEntity = dbCtx.Ceremonies.Where(c => c.CeremonyId == ceremonyId).SingleOrDefault();

                if (ceremonyEntity.Note == null || !ceremonyEntity.Note.Contains(Resources.ProtectedIdentity))
                {
                    ceremonyEntity.Note = string.IsNullOrEmpty(ceremonyEntity.Note) ? Resources.ProtectedIdentity : ceremonyEntity.Note + " " + Resources.ProtectedIdentity;
                }
                dbCtx.SubmitChanges();
            }
            catch (StockholmStadSystemException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        #endregion
    }

    internal class CeremonySpecialNeedComparer : IEqualityComparer<CeremonySpecialNeed>
    {
        #region Implementation of IEqualityComparer<CeremonySpecialNeed>

        /// <summary>
        ///                     Determines whether the specified objects are equal.
        /// </summary>
        /// <returns>
        /// true if the specified objects are equal; otherwise, false.
        /// </returns>
        /// <param name="x">
        ///                     The first object of type CeremonySpecialNeed to compare.
        ///                 </param>
        /// <param name="y">
        ///                     The second object of type CeremonySpecialNeed to compare.
        ///                 </param>
        public bool Equals(CeremonySpecialNeed x, CeremonySpecialNeed y)
        {
            return x.SpecialNeedId == y.SpecialNeedId;
        }

        /// <summary>
        ///                     Returns a hash code for the specified object.
        /// </summary>
        /// <returns>
        ///                     A hash code for the specified object.
        /// </returns>
        /// <param name="obj">
        ///                     The <see cref="T:System.Object" /> for which a hash code is to be returned.
        ///                 </param>
        /// <exception cref="T:System.ArgumentNullException">
        ///                     The type of <paramref name="obj" /> is a reference type and <paramref name="obj" /> is null.
        ///                 </exception>
        public int GetHashCode(CeremonySpecialNeed obj)
        {
            return obj.SpecialNeedId;
        }

        #endregion
    }
}